// Tomboy - A simple and easy to use note-taking application.
//
// Copyright (C) 2004-2007 Alex Graveley <alex@beatniksoftware.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
// Authors:
//	Everaldo Canuto <everaldo.canuto@gmail.com>

using System;
using Gtk;
using Hildon;
using Mono.Unix;
using System.Collections;

namespace Tomboy
{
	public class NoteWindow : Hildon.Window
	{
		// Widgets
		private Gtk.IconTheme icon_theme;
		private Gtk.TextView editor;
		private Gtk.ScrolledWindow editor_window;
		private Gtk.Menu mainmenu;
		private Gtk.Menu plugin_menu;
		private Gtk.Menu text_menu;
		private Gtk.Toolbar toolbar;
		private Gtk.ToggleToolButton find_button;
		private Gtk.ToolButton link_button;
		private Gtk.ToolButton add_button;
		private Gtk.ToolButton delete_button;
		private Gtk.ToolButton prev_button;
		private Gtk.ToolButton next_button;
		private Hildon.FindToolbar findbar;

		private Note note;

		private ArrayList showedNote;
		
		public NoteWindow (Note note) 
		{
		
			showedNote=new ArrayList();
			this.Note = note;			
			this.InitializeComponents ();
			this.ShowAll ();
		}
		
		private void InitializeComponents ()
		{
			// Menu
			mainmenu = new Menu ();
			
			// Toolbar
			this.toolbar = MakeToolbar ();
			this.AddToolbar (toolbar);

			// The main editor widget
			this.editor = new NoteEditor (note.Buffer);
			//this.editor.PopulatePopup += OnPopulatePopup;

			// editor ScrolledWindow
			editor_window = new ScrolledWindow ();
			editor_window.HscrollbarPolicy = PolicyType.Automatic;
			editor_window.VscrollbarPolicy = PolicyType.Automatic;
			editor_window.Add (editor);
			this.Add (editor_window);
			
			// Window
			this.KeyPressEvent += new Gtk.KeyPressEventHandler(OnKeyPressEvent);
			//this.DeleteEvent += new DeleteEventHandler (OnMyWindowDelete);
			//this.DeleteEvent += new DeleteEventHandler (Tomboy.NoteManager.OnExitingEvent);
						
		}
	
		private Gtk.Toolbar MakeToolbar ()
		{
			Toolbar toolbar = new Toolbar();
			
			// Separator
			SeparatorToolItem separator = new SeparatorToolItem ();
			separator.Draw = true;
			
			// find_button
			find_button = new ToggleToolButton (Catalog.GetString ("Search"));
			find_button.IconWidget = GetStockImage ("qgn_toolb_gene_findbutton", "gtk-find");
			find_button.Clicked += new EventHandler(FindButtonClicked);
			toolbar.Add (find_button);

			// link_button
			link_button = new ToolButton (Catalog.GetString ("Link"));
			link_button.StockId = "gtk-jump-to";
			link_button.Clicked += new EventHandler(LinkButtonClicked);
			link_button.Sensitive = (note.Buffer.Selection != null);
			toolbar.Add (link_button);
			
			toolbar.Add (separator); //AppendSpace ();

			// add_button
			add_button = new ToolButton (Catalog.GetString ("New"));
			add_button.IconWidget = GetStockImage ("qgn_list_gene_unknown_file", "gtk-add");
			add_button.Clicked += new EventHandler(OnNewNoteAction);
			toolbar.Add (add_button);

			// delete_button
			delete_button = new ToolButton (Catalog.GetString ("Delete"));
			delete_button.IconWidget = GetStockImage ("qgn_toolb_gene_deletebutton", "gtk-delete");
			delete_button.Clicked += new EventHandler(DeleteButtonClicked);
			//delete_button.Sensitive = !note.IsSpecial;
			toolbar.Add (delete_button);
		
			SeparatorToolItem separator2 = new SeparatorToolItem ();
			separator2.Draw =	 true;
			toolbar.Add (separator2); //AppendSpace ();

			// prev_button
			prev_button = new ToolButton (Catalog.GetString ("Prev"));
			prev_button.IconWidget = GetStockImage ("qgn_list_gene_back", "gtk-go-back");
			prev_button.Clicked += new EventHandler(PreviousButtonClicked);
			prev_button.Sensitive=false;
			toolbar.Add (prev_button);

			// next_button
			next_button = new ToolButton (Catalog.GetString ("Next"));
			next_button.IconWidget = GetStockImage ("qgn_list_gene_forward", "gtk-go-forward");
			next_button.Clicked += new EventHandler(NextButtonClicked);
			next_button.Sensitive=false;
			toolbar.Add (next_button);
		
			SeparatorToolItem separator3 = new SeparatorToolItem ();
			separator3.Draw =	 true;
			toolbar.Add (separator3); //AppendSpace ();

			// next_button
			Gtk.ToolButton toc_button = new ToolButton (Catalog.GetString ("ToC"));
			toc_button.IconWidget = GetStockImage ("qgn_toolb_gene_find", "gtk-find2");
			toc_button.Clicked += new EventHandler(ToCButtonClicked);
			toc_button.Sensitive=true;
			toolbar.Add (toc_button);
		
			return toolbar;
		}

		private Gtk.Image GetStockImage (string icon, string stock)
		{
			if (icon_theme == null)
				icon_theme = new IconTheme ();
			
			Gdk.Pixbuf pixbuf = icon_theme.LoadIcon(icon, 26, IconLookupFlags.NoSvg);
			
			Gtk.Image image;
			
			if (pixbuf == null)
				image = new Gtk.Image (icon, IconSize.LargeToolbar);
			else
				image = new Gtk.Image (pixbuf);
			
			return image; 
		}
		
		protected override bool OnDeleteEvent (Gdk.Event evnt)
		{
			if (note != null)
				note.Save ();

			Application.Quit ();
			return true;
		}

		protected virtual void OnKeyPressEvent(object o, Gtk.KeyPressEventArgs args)
		{
			switch (args.Event.Key)
			{
	 			case Gdk.Key.F6:  
 					this.FullScreen = !this.FullScreen;
					args.RetVal = true;
					break;
				default:
					args.RetVal = false;
					break;
			}
		}

		bool handleLoopEvent=false;
		protected void FindButtonClicked (object source, EventArgs e)
		{
			
			if (!handleLoopEvent) {
				handleLoopEvent=true;
				if (findbar == null) {
					findbar = new FindToolbar (Catalog.GetString ("Search") + ":");
					findbar.Closed += new EventHandler (FindButtonClicked);
					findbar.Search += new EventHandler (findbar_search);
					//findbar.InvalidInput += new EventHandler (findbar_invalid_input);
					//findbar.HistoryAppend += new EventHandler (findbar_history_append);

					this.AddToolbar (findbar);
				}
				
				findbar.Visible = !findbar.Visible;
				find_button.Active = findbar.Visible; 
				handleLoopEvent=false;
			}
		}
		
		protected void findbar_search (object source, EventArgs e)
		{
			SearchText = findbar.Prefix;
			PerformSearch(true);
		}
		
		protected void LinkButtonClicked (object source, EventArgs evnt)
		{
			string select = note.Buffer.Selection;
			
			if (select == null)
				return;
			
			string body_unused;
			string title = NoteManager.SplitTitleFromContent (select, out body_unused);
			if (title == null)
				return;

			Note match = note.Manager.Find (title);
			if (match == null) {
				try {
					match = note.Manager.Create (select);
				} catch (Exception e) {
					HIGMessageDialog dialog = 
						new HIGMessageDialog (
							this,
							Gtk.DialogFlags.DestroyWithParent,
							Gtk.MessageType.Error,
							Gtk.ButtonsType.Ok,
							Catalog.GetString ("Cannot create note"),
							e.Message);
					dialog.Run ();
					dialog.Destroy ();
					return;
				}
			}

			//match.Window.Present ();
			this.Note = match;
		}
		
		void OnSelectionMarkSet (object sender, Gtk.MarkSetArgs args)
		{
			link_button.Sensitive = (note.Buffer.Selection != null);
		}

		#region Properties

		public bool FullScreen {
			get { return (GdkWindow.State == Gdk.WindowState.Fullscreen); }
			set {
				if ((GdkWindow.State == Gdk.WindowState.Fullscreen) && value)
					return;

				if (value)
					Fullscreen();
				else
					Unfullscreen();
			}
		}

		private Note Note {
			get { return note; }
			set {

				int npos=0;
				
				if (showedNote!=null) {

					// Check if the note is alreay in the history
					npos=showedNote.IndexOf(value);
					if (npos>0) {
						if (npos>0 && showedNote[npos-1]==note) {
						} else if (npos>=0 && npos<showedNote.Count-1) {
						} else {
							showedNote.RemoveRange(0,npos);
							next_button.Sensitive=false;
						}
					} else {
					
						// Check if the last note is the first in the history
						npos=showedNote.IndexOf(note);
						if (npos>0) {

							// Remove all the note in the histore beetwen the first and 
							// last showed
							showedNote.RemoveRange(0,npos);
							next_button.Sensitive=false;
						}
						showedNote.Insert(0,value);
						if (prev_button!=null) {
							if (showedNote.Count>0) {
								prev_button.Sensitive=true;
							} else {
								prev_button.Sensitive=false;
							}
						}
					}
				}
				
				if (note == value)
					return;

				note = value;
				note.Buffer.MarkSet += OnSelectionMarkSet;
				Title = note.Title;

				if (editor != null) {
					editor.Buffer = note.Buffer;
//					if (showedNote.Contains(note)) {
//						showedNote.Remove(note);
//					}
//					showedNote.Insert(0,note);
				}
			}
		}

		public Gtk.TextView Editor {
			get { return editor; }
		}

		public Gtk.Menu PluginMenu {
			get { return plugin_menu; }
		}

		public Gtk.Menu TextMenu {
			get { return text_menu; }
		}
		
		public Gtk.Toolbar Toolbar {
			get { return toolbar; }
		}
		
		#endregion // Properties

		#region Search_Functionality

		ArrayList current_matches;
		String SearchText=null;
		String prev_search_text=null;
		
		void PerformSearch (bool scroll_to_hit)
		{
			CleanupMatches ();

			string text = SearchText;
			if (text == null)
				return;

//			if (!case_sensitive.Active)
				text = text.ToLower ();

			string [] words = text.Split (' ', '\t', '\n');

			current_matches = 
				FindMatchesInBuffer (note.Buffer, 
						     words,
						     false);	
//						     case_sensitive.Active);
			
			prev_search_text = SearchText;

			if (current_matches != null) {
				HighlightMatches (true);
				
				// Select/scroll to the first match
//				if (scroll_to_hit)
//					OnNextClicked (this, EventArgs.Empty);
			}

			UpdateSensitivity ();
		}
		
		ArrayList FindMatchesInBuffer (NoteBuffer buffer, string [] words, bool match_case)
		{
			ArrayList matches = new ArrayList ();

			string note_text = buffer.GetText (buffer.StartIter, 
							   buffer.EndIter, 
							   false /* hidden_chars */);
			if (!match_case)
				note_text = note_text.ToLower ();

			foreach (string word in words) {
				int idx = 0;
				bool this_word_found = false;

				if (word == String.Empty)
					continue;

				while (true) {					
					idx = note_text.IndexOf (word, idx);

					if (idx == -1) {
						if (this_word_found)
							break;
						else
							return null;
					}

					this_word_found = true;

					Gtk.TextIter start = buffer.GetIterAtOffset (idx);
					Gtk.TextIter end = start;
					end.ForwardChars (word.Length);

					Match match = new Match ();
					match.Buffer = buffer;
					match.StartMark = buffer.CreateMark (null, start, false);
					match.EndMark = buffer.CreateMark (null, end, true);
					match.Highlighting = false;

					matches.Add (match);

					idx += word.Length;
				}
			}

			if (matches.Count == 0)
				return null;
			else
				return matches;
		}

		void UpdateSensitivity ()
		{
			if (SearchText == null) {
//				next_button.Sensitive = false;
//				prev_button.Sensitive = false;
			}
			
			if (current_matches != null && current_matches.Count > 0) {
//				next_button.Sensitive = true;
//				prev_button.Sensitive = true;
			} else {
//				next_button.Sensitive = false;
//				prev_button.Sensitive = false;
			}
		}

		void HighlightMatches (bool highlight)
		{
			if (current_matches == null || current_matches.Count == 0)
				return;
			
			foreach (Match match in current_matches) {
				NoteBuffer buffer = match.Buffer;
				
				if (match.Highlighting != highlight) {
					Gtk.TextIter start = buffer.GetIterAtMark (match.StartMark);
					Gtk.TextIter end = buffer.GetIterAtMark (match.EndMark);
					
					match.Highlighting = highlight;
					
					if (match.Highlighting)
						buffer.ApplyTag ("find-match", start, end);
					else
						buffer.RemoveTag ("find-match", start, end);
				}
			}
		}

		// Method do manage the search functions
		void CleanupMatches ()
		{
			if (current_matches != null) {
				HighlightMatches (false /* unhighlight */);
				
				foreach (Match match in current_matches) {
					match.Buffer.DeleteMark (match.StartMark);
					match.Buffer.DeleteMark (match.EndMark);
				}
				
				current_matches = null;
			}
			
			UpdateSensitivity ();
		}
		
		class Match 
		{
			public NoteBuffer   Buffer;
			public Gtk.TextMark StartMark;
			public Gtk.TextMark EndMark;
			public bool         Highlighting;
		}
		
		#endregion

		#region Add_New_Note
		
		private void OnNewNoteAction (object sender, EventArgs args)
		{
			try {
				Note new_note = note.Manager.Create ();
				new_note.Window.Show ();
			} catch (Exception e) {
				HIGMessageDialog dialog = 
					new HIGMessageDialog (
						null,
						0,
						Gtk.MessageType.Error,
						Gtk.ButtonsType.Ok,
						Catalog.GetString ("Cannot create new note"),
						e.Message);
				dialog.Run ();
				dialog.Destroy ();
			}
		}
		#endregion
		
		#region Del_Note
		
		//
		// Delete this Note.
		//

		void DeleteButtonClicked (object sender, EventArgs args) 
		{
			// Prompt for note deletion
			NoteUtils.ShowDeletionDialog (note, this);
			if (note.Data.deleted) {
				showedNote.Remove(note);
				if (showedNote.Count>0) {
					Note = (Note)showedNote[0];
				}
			}
		}

		#endregion

		//
		// Show the previous note in the list.
		//

		void PreviousButtonClicked (object sender, EventArgs args) 
		{
			int nrec=showedNote.Count;

			// Find the current node in the history
			int pos=showedNote.IndexOf(note);
			
			if (pos>=0 && pos<nrec-1) {
				Note = (Note)showedNote[pos+1];			

				// Test if the button bust be enabled
				if (pos+1<showedNote.Count-1) {
					prev_button.Sensitive=true;
				} else {
					prev_button.Sensitive=false;
				}
				next_button.Sensitive=true;
			}

			/*
			int nrec=showedNote.Count;
			showedNote.Remove(note);
			
			if (nrec < 1 || nrec!=showedNote.Count) {
				showedNote.Add(note);
			}
			
			if (showedNote.Count>0) {
				Note = (Note)showedNote[0];
			}
			*/
		}

		//
		// Show the Next note in the list.
		//

		void NextButtonClicked (object sender, EventArgs args) 
		{
			int nrec=showedNote.Count;

			// Find the current node in the history
			int pos=showedNote.IndexOf(note);
			
			if (pos>=0 && pos>0) {
				Note = (Note)showedNote[pos-1];			

				// Test if the button bust be enabled
				if (pos-1>0) {
					next_button.Sensitive=true;
				} else {
					next_button.Sensitive=false;
				}
				prev_button.Sensitive=true;
			}
/*		
			int nrec=showedNote.Count;
			showedNote.Remove(note);
			
			if (nrec < 1 || nrec!=showedNote.Count) {
				showedNote.Insert(0,note);
			}
			
			if (showedNote.Count>0) {
				Note = (Note)showedNote[showedNote.Count-1];
			}
*/
		}

		void ToCButtonClicked (object sender, EventArgs args) 
		{
			ToCWindow toc=new ToCWindow(note.manager);
			
			toc.TransientFor=this;

			// Set the modal style
			toc.Modal=true;
			toc.WindowPosition=Gtk.WindowPosition.CenterOnParent;
		
			toc.Hidden += new EventHandler(chooseANoteEvent);
			toc.ShowAll();
			toc.Show();
		}

		public void chooseANoteEvent(object o, EventArgs args) {
			ToCWindow toc=(ToCWindow)o;
			
			if (toc.note!=null) {
				Note=toc.note;
			}
		}
	}
}
